package compiler

import (
	"equantum/common"
	"equantum/model"
	"log"
	"math"
	"runtime"
	"strconv"

	"github.com/gin-gonic/gin"
)

/* 输入 */
var qasm string // 输入代码字符串
var inoff int   // 输入代码字符串偏移量
var lineno int  // TODO：报错输出行号

/* 词语法分析 */
var ch byte              // 存放当前读取的字符
var sym int              // 当前的符号
var id string            // 当前ident
var num int              // 当前number
var flo float64          // 浮点数
var gate string          // 当前量子门
var word [norw]string    // 保留字
var ssym [nssym]int      // 单字符对应的符号值
var wsym [norw]int       // 保留字对应的符号值
var table [txmax]table_t // 符号表

/* 出错处理 */
var emsg [nerr]string // 错误信息提示

/* 目标线路生成 */
var stk [stkmax]float64   // 数据栈，计算gate的参数
var params []float64      // 存储gate的参数
var paramsIdx int         // params数组下标索引
var sp int                // 栈顶指针
var nqubit int            // 线路量子比特数
var circuit [][]QGate     // 线路结构
var inGateDecla bool      // 是否在自定义量子门声明中
var nqubitGate int        // 自定义量子门比特数
var circuitGate [][]QGate // 自定义量子门线路

func fill_circuit(inputs []float64) {
	// 1. 找到input序列里最小的和最大的比特号
	minIdx, maxIdx := 999, 0
	for i := 0; i < len(inputs); i++ {
		input := int(inputs[i])
		if input < minIdx {
			minIdx = input
		}
		if input > maxIdx {
			maxIdx = input
		}
	}
	// 2. 获取[minIdx, maxIdx]范围内最长的那根线路的长度
	maxLen := 0
	for i := minIdx; i <= maxIdx; i++ {
		if len(circuit[i]) > maxLen {
			maxLen = len(circuit[i])
		}
	}
	// 3. [minIdx, maxIdx]范围内的全部补0到对齐
	for i := minIdx; i <= maxIdx; i++ {
		for len(circuit[i]) < maxLen {
			circuit[i] = append(circuit[i], QGate{ID: 0})
		}
		if i != num { // 只要留一个空放gate，其他还是补空
			circuit[i] = append(circuit[i], QGate{ID: 0})
		}
	}
}

/**
 * 输入：Quantum Assembly代码(QASM)
 * 输出：线路json
 */
func Compile(input string) {
	qasm = input + "."
	log.Printf("qasm: \n%v", qasm)

	// 初始化协程通信通道
	Channel = make(chan gin.H, 3)

	// 初始化符号、错误信息
	init_symbol()

	// 空输入文件
	if len(qasm) == 0 {
		error(1) // 空文件
	}

	// 初始化全局变量
	params = make([]float64, parmax, parmax)
	ch, inoff, lineno = ' ', 0, 1
	sp, nqubit, nqubitGate, inGateDecla = 0, 0, 0, false
	circuit = nil
	circuitGate = nil

	getsym()

	tx := 0
	program(&tx)

	if sym != stop {
		error(16)
	}

	Channel <- gin.H{"nqubit": nqubit, "circuit": circuit}

	Wg.Done()
}

/**
 * 初始化可识别单词符号
 */
func init_symbol() {
	for i := 0; i < nssym; i++ {
		ssym[i] = nul
	}
	/* 设置单字符符号 */
	ssym['+'] = plus
	ssym['-'] = minus
	ssym['*'] = times
	ssym['/'] = slash
	ssym['('] = lparen
	ssym[')'] = rparen
	ssym['['] = lbrack
	ssym[']'] = rbrack
	ssym['{'] = lbrace
	ssym['}'] = rbrace
	ssym['.'] = stop
	ssym[','] = comma
	ssym[';'] = semicolon

	/* 设置保留字名字 */
	word[0] = "qreg"
	word[1] = "gate"
	word[2] = "id"
	word[3] = "x"
	word[4] = "y"
	word[5] = "z"
	word[6] = "h"
	word[7] = "s"
	word[8] = "sdg"
	word[9] = "t"
	word[10] = "tdg"
	word[11] = "cx"
	word[12] = "cy"
	word[13] = "cz"
	word[14] = "ch"
	word[15] = "swap"
	word[16] = "ccx"
	word[17] = "cswap"
	word[18] = "u"
	word[19] = "rx"
	word[20] = "ry"
	word[21] = "rz"
	word[22] = "cu"
	word[23] = "measure"
	word[24] = "pi"

	/* 设置保留字符号 */
	wsym[0] = qregsym
	wsym[1] = gatesym
	wsym[2] = idgate
	wsym[3] = xgate
	wsym[4] = ygate
	wsym[5] = zgate
	wsym[6] = hgate
	wsym[7] = sgate
	wsym[8] = sdggate
	wsym[9] = tgate
	wsym[10] = tdgate
	wsym[11] = cxgate
	wsym[12] = cygate
	wsym[13] = czgate
	wsym[14] = chgate
	wsym[15] = swapgate
	wsym[16] = ccxgate
	wsym[17] = cswapgate
	wsym[18] = ugate
	wsym[19] = rxgate
	wsym[20] = rygate
	wsym[21] = rzgate
	wsym[22] = cugate
	wsym[23] = measurezgate
	wsym[24] = floating

	/* 设置出错信息提示 */
	emsg[1] = "空文件"
	emsg[2] = "缺少量子寄存器声明语句"
	emsg[3] = "缺少分号"
	emsg[4] = "缺少左中括号"
	emsg[5] = "缺少右中括号"
	emsg[6] = "缺少整数"
	emsg[7] = "缺少标识符"
	emsg[8] = "标识符未声明"
	emsg[9] = "数组访问越界"
	emsg[10] = "缺少左大括号"
	emsg[11] = "缺少右大括号"
	emsg[12] = "标识符重定义"
	emsg[13] = "缺少左小括号"
	emsg[14] = "缺少右小括号"
	emsg[15] = "缺少数字"
	emsg[16] = "编译失败"
	emsg[17] = "参数过多"
}

/**
 * 报错
 */
func error(n int) {
	Channel <- gin.H{"msg": "第" + strconv.Itoa(lineno) + "行：" + emsg[n]}
	Wg.Done()        // 释放锁
	runtime.Goexit() // 退出协程
}

/**
 * 读取一个字符
 * 被getsym()调用
 */
func getch() {
	if inoff == len(qasm) {
		return // EOF
	}
	ch = qasm[inoff]
	inoff++
	if ch == '\n' {
		lineno++
	}
}

/**
 * 词法分析，获取一个符号
 */
func getsym() {
	// 过滤空格、换行符、制表符、注释
	for ch == ' ' || ch == '\n' || ch == '\r' || ch == '\t' || ch == '/' {
		if ch == '/' {
			getch()
			if ch == '/' { // 过滤注释
				getch()
				for ch != '\n' && ch != '\r' && ch != '.' { // 注释行还未结束
					getch()
				}
			} else { // 非注释，退出
				sym = slash
				return
			}
		} else {
			getch()
		}
	}
	// 标识符或保留字
	if ('a' <= ch && ch <= 'z') || ('A' <= ch && ch <= 'Z') {
		a := string(ch)
		getch()
		for ('a' <= ch && ch <= 'z') || ('A' <= ch && ch <= 'Z') || ('0' <= ch && ch <= '9') {
			a += string(ch)
			getch()
		}
		id = a
		// 搜索判断是否是保留字
		var i int = 0
		for i = 0; i < norw; i++ {
			if id == word[i] {
				sym = wsym[i]   // 保留字编号
				if id == "pi" { // 特殊处理pi
					flo = math.Pi
				}
				break
			}
		}
		if i == norw {
			sym = ident // 不是保留字，是标识符
		}
	} else if '0' <= ch && ch <= '9' { // 数字
		a := string(ch)
		getch()
		isInt := true
		for ('0' <= ch && ch <= '9') || ch == '.' {
			if ch == '.' {
				isInt = false
			}
			a += string(ch)
			getch()
		}
		if isInt {
			sym = integer
			num, _ = strconv.Atoi(a)
		} else {
			sym = floating
			flo, _ = strconv.ParseFloat(a, 64)
		}
	} else { // 单分界符或不能识别的符号
		sym = ssym[int(ch)]
		getch()
	}
}

/*
 * 在符号表中加入一项
 * k:      标识符的种类
 * ptx:    符号表尾指针的指针，为了可以改变符号表尾指针的值
 */
func enter(k int, ptx *int) {
	(*ptx)++
	table[*ptx].name = id // 标识符名字
	table[*ptx].kind = k
	table[*ptx].val = num // 寄存器大小 / 门参数个数 / 参数次序
}

/**
 * 查找标识符在符号表中的位置，从tx开始倒序查找标识符
 * @param id	要查找的名字
 * @param tx	当前符号表尾指针
 * @return		找到则返回在符号表中的位置，否则返回0
 */
func position(id string, k int, tx int) int {
	i := tx
	table[0].name = id
	table[0].kind = k
	for table[i].name != id || table[i].kind != k {
		i--
	}
	return i
}

/**
 * 编译程序主体
 * @param ptx	符号表当前尾指针
 */
func program(ptx *int) {
	declaration_list(ptx) // 声明
	log.Printf("=== declaration ===")
	statement_list(ptx) // 线路搭建
	log.Printf("=== statement ===")
	measurement_list(ptx) // 测量
	log.Printf("=== measurement ===")
}

// 声明语句
func declaration_list(ptx *int) {
	qreg_declaration(ptx)
	for sym == gatesym {
		gate_declaration(ptx)
	}
}

// 量子寄存器声明qreg q[x];
func qreg_declaration(ptx *int) {
	if sym != qregsym {
		error(2) // 缺少量子寄存器声明语句
	}
	getsym()
	qubit(ptx, decla)
	if sym == semicolon {
		getsym()
	} else {
		error(3) // 缺少分号
	}
}

// 量子比特q[x]
func qubit(ptx *int, typ int) {
	// 1. 分解完整个语句
	if sym == ident {
		getsym()
		if sym == lbrack {
			getsym()
			if sym == integer {
				getsym()
				if sym != rbrack {
					error(5) // 缺少右中括号
				}
			} else {
				error(6) // 缺少整数
			}
		} else {
			error(4) // 缺少左中括号
		}
	} else {
		error(7) // 缺少标识符
	}
	// 2. 根据不同情况处理
	switch typ {
	// Case 1: 声明量子寄存器
	case decla:
		if !inGateDecla {
			// (1.1) 线路的量子寄存器声明，在最开始，不需要检查重复声明
			enter(qreg_t, ptx) // 插入符号表
			nqubit = num       // 记录线路的量子比特数
			circuit = make([][]QGate, num)
		} else {
			// (1.2) 自定义量子门的量子寄存器声明，使用范围仅在gate内部，不需要检查重复声明
			enter(greg_t, ptx)
			nqubitGate = num
			circuitGate = make([][]QGate, num)
		}
		break
	// Case 2: 访问量子比特
	case use:
		if !inGateDecla {
			// (2.1) 线路中访问量子比特
			i := position(id, qreg_t, *ptx)
			if i == 0 {
				error(8) // 标识符未声明
			}
			// 判断量子寄存器访问是否越界
			if 0 <= num && num < table[i].val {
				// 记录输入的量子比特编号
				params[paramsIdx] = float64(num)
				paramsIdx++
				// 在第num个qubit上施加量子门(从数据库获取构建circuit的必要gate信息)
				var basicGate model.Gate
				db := common.GetDB()
				if err := db.Where("name = ?", gate).First(&basicGate).Error; err != nil {
					error(16)
				}
				// 查看多输入比特的门的输入是不是都处理完了
				if paramsIdx == basicGate.Param+basicGate.Input {
					// 处理完了，不用继续读入输入q[x]
					paramsTmp := make([]float64, basicGate.Param+basicGate.Input)
					copy(paramsTmp, params[:basicGate.Param+basicGate.Input])
					g := QGate{
						ID:     basicGate.ID,
						Name:   basicGate.Name,
						Type:   basicGate.Type,
						Input:  basicGate.Input,
						Param:  basicGate.Param,
						Params: paramsTmp,
						UserID: basicGate.UserID,
						Code:   basicGate.Code,
					}
					fill_circuit(params[basicGate.Param : basicGate.Param+basicGate.Input]) // 补全线路
					// 放置量子门
					circuit[num] = append(circuit[num], g)
				}
			} else {
				error(9) // 数组访问越界
			}
		} else {
			// (2.2) 量子门定义中访问量子比特
			i := position(id, greg_t, *ptx)
			if i == 0 {
				error(8) // 标识符未声明
			}
			if 0 <= num && num < table[i].val {
				// TODO：在第num个qubit上施加量子门
				var basicGate model.Gate
				db := common.GetDB()
				if err := db.Where("name = ?", gate).First(&basicGate).Error; err != nil {
					error(16)
				}
				g := QGate{
					ID:     basicGate.ID,
					Name:   basicGate.Name,
					Type:   basicGate.Type,
					Param:  basicGate.Param,
					Params: params,
				}
				circuitGate[num] = append(circuitGate[num], g)
			} else {
				error(9) // 数组访问越界
			}
		}
		break
	}
	getsym() // 走出qubit语句
}

// gate xx(a, b, c) { [qreg_declaration] [statement_list] }
func gate_declaration(ptx *int) {
	inGateDecla = true
	tx0 := *ptx // 记录当前的符号表地址

	getsym()
	// 处理gate名字
	if sym == ident { // 声明gate名字
		gate = id
		num = 0 // 记录gate参数个数
		getsym()
	} else {
		error(7) // 缺少标识符
	}
	// 处理gate的参数
	if sym == lparen {
		getsym()
		// 第一个param
		if sym == ident {
			i := position(id, param_t, *ptx)
			if i == 0 {
				enter(param_t, ptx) // 插入参数
				num++
				getsym()
			} else {
				error(12) // 标识符重定义
			}
		} else {
			error(7) // 缺少标识符
		}
		// 多个param
		for sym == comma {
			getsym()
			if sym == ident {
				i := position(id, param_t, *ptx)
				if i == 0 {
					enter(param_t, ptx) // 插入参数
					num++
					getsym()
				} else {
					error(12) // 标识符重定义
				}
			} else {
				error(7) // 缺少标识符
			}
		}
		// )
		if sym == rparen {
			getsym()
		} else {
			error(14) // 缺少右小括号
		}
	}
	// 处理定义体
	if sym == lbrace {
		id = gate
		enter(gate_t, ptx) // 插入gate名字和gate参数个数
		getsym()
	} else {
		error(10) // 缺少左大括号
	}
	qreg_declaration(ptx)
	statement_list(ptx)
	if sym == rbrace {
		getsym()
	} else {
		error(11) // 缺少右大括号
	}

	// TODO：写数据库，记录新增的门信息，name字段unique
	// 名字，参数个数，qubit个数(nqubitGate)，线路(circuitGate)
	log.Printf("new gate %v(%v): nqubit:%v, circuit:%v", table[*ptx-1].name, table[*ptx-1].val, table[*ptx].val, circuitGate)

	*ptx = tx0 // 弹出gate_declaration中声明的变量信息
	log.Printf("after pop tx: %v (expected: 1)", *ptx)
	inGateDecla = false
}

// statement_list
func statement_list(ptx *int) {
	for sym != measurezgate && sym != stop && sym != rbrace {
		statement(ptx)
	}
}

// 语句 GATE[(p1{, p2})] ID[NUM]{, ID[NUM]};
func statement(ptx *int) {
	gate = id // 记录gate名字 // TODO：检查sym是不是保留字，不是的话说明是自定义gate
	getsym()
	paramsIdx = 0
	// 处理gate参数
	if sym == lparen {
		getsym()
		// 第一个参数
		expression(ptx)
		log.Printf("param: stk[%v] = %v", sp, stk[sp])
		params[paramsIdx] = stk[sp]
		paramsIdx++
		sp--
		// 多个参数
		for sym == comma {
			getsym()
			expression(ptx)
			log.Printf("param: stk[%v] = %v", sp, stk[sp])
			if paramsIdx == parmax {
				error(17)
			}
			params[paramsIdx] = stk[sp]
			paramsIdx++
			sp--
		}
		// 右括号
		if sym == rparen {
			getsym()
		} else {
			error(14) // 缺少右小括号
		}
	}
	// 处理量子比特q[x]
	qubit(ptx, use) // 第一个量子比特
	for sym == comma {
		getsym()
		qubit(ptx, use)
	}
	// ;
	if sym == semicolon {
		getsym()
	} else {
		error(3)
	}
}

// 参数表达式
func expression(ptx *int) {
	term(ptx)
	for sym == plus || sym == minus {
		rop := sym // 保存运算符
		getsym()
		term(ptx)
		sp--
		if rop == plus {
			stk[sp] = stk[sp] + stk[sp+1]
		} else {
			stk[sp] = stk[sp] - stk[sp+1]
		}
	}
}

func term(ptx *int) {
	factor(ptx)
	for sym == times || sym == slash {
		rop := sym // 保存运算符
		getsym()
		factor(ptx)
		sp--
		if rop == times {
			stk[sp] = stk[sp] * stk[sp+1]
		} else {
			stk[sp] = stk[sp] / stk[sp+1]
		}
	}
}

func factor(ptx *int) {
	switch sym {
	case lparen:
		getsym()
		expression(ptx)
		if sym == rparen {
			getsym()
		} else {
			error(14) // 缺少右小括号
		}
		break
	case integer:
		// 从num中获取值
		sp++
		stk[sp] = float64(num)
		getsym()
		break
	case floating:
		// 从flo中获取值
		sp++
		stk[sp] = flo
		getsym()
		break
	case ident:
		// gate定义中的参数
		// 从table中找到参数是第几个
		// TODO：构造写数据库的qgate_t
		getsym()
		break
	default:
		error(15) // 缺少数字
		break
	}
}

func measurement_list(ptx *int) {
	for sym == measurezgate {
		measurement(ptx)
	}
}

func measurement(ptx *int) {
	gate = id // measure
	getsym()
	paramsIdx = 0
	qubit(ptx, use)
	if sym == semicolon {
		getsym()
	} else {
		error(3)
	}
}
